/*
 * File: BouncingBall.java
 * ========================================================
 * A program that simulates a bouncing ball.  This program
 * now allows the user to control the parameters of the
 * experiment.
 */
import acm.program.*;
import acm.graphics.*;

import java.awt.*;
import javax.swing.*;

public class BouncingBall extends GraphicsProgram {
	/* The initial X velocity of the ball. */
	private static final double INITIAL_SPEED = 5.0;
	
	/* How long to delay between frames. */
	private static final double PAUSE_TIME = 1000.0 / 48;
	
	/* The size of the ball. */
	private static final double BALL_SIZE = 50;
	
	/* Two slider controls that control gravity and elasticity. */
	private JSlider gravityInput;
	private JSlider elasticityInput;
	
	public void init() {
		/* Set up the gravity and elasticity sliders. */
		gravityInput = new JSlider(SwingConstants.VERTICAL, 0, 200, 100);
		elasticityInput = new JSlider(SwingConstants.VERTICAL, 0, 100, 75);
		
		/* Add the two sliders. */
		add(gravityInput, WEST);
		add(elasticityInput, EAST);
	}
	
	/* Simulates a bouncing ball. */
	public void run() {
		while (true) {
			GOval ball = createBall();
			add(ball);
			bounceBall(ball);
			removeAll();
		}
	}
	
	/**
	 * Creates the ball that will be dropped.
	 * 
	 * @return The ball that will be dropped.
	 */
	private GOval createBall() {
		GOval ball = new GOval(0, 0, BALL_SIZE, BALL_SIZE);
		ball.setFilled(true);
		ball.setColor(Color.BLUE);
		return ball;
	}
	
	/**
	 * Simulates the ball dropping.
	 * 
	 * @param ball The ball to drop.
	 */
	private void bounceBall(GOval ball) {	
		/* Track the ball's velocity. */
		double dx = INITIAL_SPEED;
		double dy = 0;
		
		/* Until the ball hits the ground, keep simulating the ball falling. */
		while (isBallOnScreen(ball)) {
			ball.move(dx, dy);
			
			/* Use the sliders to determine gravity or elasticity. */
			if (isBallAboveGround(ball)) {
				dy += gravityInput.getValue() / 100.0;
			} else {
				dy *= -elasticityInput.getValue() / 100.0;
				moveBallOutOfGround(ball);
			}
						
			pause(PAUSE_TIME);
		}
	}
	
	/**
	 * Returns whether the given ball is on the screen.
	 * 
	 * @param ball The ball to check.
	 * @return Whether it's on-screen.
	 */
	private boolean isBallOnScreen(GOval ball) {
		return ball.getX() < getWidth();
	}
	 
	/**
	 * Moves the ball out of the ground.
	 * 
	 * @param ball The ball to move out of the ground.
	 */
	private void moveBallOutOfGround(GOval ball) {
		ball.move(0, getHeight() - (ball.getY() + ball.getHeight()));
	}
	
	/**
	 * Returns whether the ball is above ground.
	 * 
	 * @param ball The ball to check.
	 * @return Whether it's above ground.
	 */
	private boolean isBallAboveGround(GOval ball) {
		return ball.getY() + ball.getHeight() < getHeight();
	}
}
